package com.cx.common.gateway.filter.post;

import com.alibaba.fastjson2.JSON;
import com.cx.common.gateway.filter.AbstractFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * 打印请求参数及统计执行时长过滤器
 */
@Slf4j
@Component
public class LoggingFilter extends AbstractFilter implements Ordered {

    /**
     * 慢方法消耗时间500ms
     */
    private static final long SLOW_METHOD_COST_TIME = 500;
    private static final String REQUEST_START_TIME = "REQUEST_START_TIME";
    private static final String PROD_ENV_STRING = "prod";
    private final String profile;

    public LoggingFilter(RouteLocator routeLocator,
                         @Value("${spring.profiles.active:dev}") String profile) {
        super(routeLocator);
        this.profile = profile;
    }

    @Override
    public int getOrder() {
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public Mono<Void> run(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(REQUEST_START_TIME, System.currentTimeMillis());
        final ServerHttpRequest request = exchange.getRequest();
        final ServerHttpResponse response = exchange.getResponse();

        HttpMethod method = request.getMethod();
        HttpHeaders headers = request.getHeaders();

        StringBuilder logLine = new StringBuilder("\n========================================== Start ==========================================");
        logLine.append("\nURL                : ").append(request.getURI());
        logLine.append("\nHTTP Method        : ").append(method);
        logLine.append("\nRequest Header     : ").append(JSON.toJSONString(headers));
        logLine.append("\nRequest IP         : ").append(getIpAddress(request));
        logLine.append("\nQuery Params       : ").append(JSON.toJSONString(request.getQueryParams()));

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            String requestBody = exchange.getAttribute(CLIENT_REQUEST_BODY);
            if (Objects.nonNull(requestBody)) {
                logLine.append("\nReq Body           : ").append(requestBody);
            }

            logLine.append("\nRes Header         : ").append(JSON.toJSONString(response.getHeaders()));
            String result = exchange.getAttribute(SERVER_RESPONSE_RESULT);
            if (Objects.nonNull(result)) {
                logLine.append("\nRes Result         : ").append(result);
            }
            String encryptResult = exchange.getAttribute(SERVER_RESPONSE_ENCRYPT_RESULT);
            if (Objects.nonNull(encryptResult)) {
                logLine.append("\nRes encryptResult  : ").append(encryptResult);
            }

            long cost = -1;
            Long startTime = exchange.getAttribute(REQUEST_START_TIME);
            if (Objects.nonNull(startTime)) {
                cost = System.currentTimeMillis() - startTime;
                logLine.append("\nTime Cost          : ").append(cost).append(" ms");
            }

            logLine.append("\n=========================================== End ===========================================\n");
            if (cost >= SLOW_METHOD_COST_TIME) {
                log.warn("请求超过{}ms啦！！！ {}", SLOW_METHOD_COST_TIME, logLine.toString());
            } else {
                if (PROD_ENV_STRING.equals(profile)) {
                    log.debug(logLine.toString());
                } else {
                    log.info(logLine.toString());
                }
            }
        }));
    }


    /**
     * 获取真实ip地址
     *
     * @param request
     * @return
     */
    public String getIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();

        String unknown = "unknown";
        String ip = headers.getFirst("proxy_add_x_forwarded_for");
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        // 如果是多级代理，那么取第一个ip为客户端ip
        if (ip != null && ip.contains(",")) {
            ip = ip.substring(0, ip.indexOf(",")).trim();
        }

        return ip;
    }
}
